1 /*
2 * Copyright (C) 2008 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.common.base.internal;
18
19 import java.lang.ref.PhantomReference;
20 import java.lang.ref.Reference;
21 import java.lang.ref.ReferenceQueue;
22 import java.lang.ref.WeakReference;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 /**
29 * Thread that finalizes referents. All references should implement
30 * {@code com.google.common.base.FinalizableReference}.
31 *
32 * <p>While this class is public, we consider it to be *internal* and not part
33 * of our published API. It is public so we can access it reflectively across
34 * class loaders in secure environments.
35 *
36 * <p>This class can't depend on other Google Collections code. If we were
37 * to load this class in the same class loader as the rest of
38 * Google Collections, this thread would keep an indirect strong reference
39 * to the class loader and prevent it from being garbage collected. This
40 * poses a problem for environments where you want to throw away the class
41 * loader. For example, dynamically reloading a web application or unloading
42 * an OSGi bundle.
43 *
44 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
45 * in its own class loader. That way, this class doesn't prevent the main
46 * class loader from getting garbage collected, and this class can detect when
47 * the main class loader has been garbage collected and stop itself.
48 */
49 public class Finalizer implements Runnable {
50
51 private static final Logger logger
52 = Logger.getLogger(Finalizer.class.getName());
53
54 /** Name of FinalizableReference.class. */
55 private static final String FINALIZABLE_REFERENCE
56 = "com.google.common.base.FinalizableReference";
57
58 /**
59 * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
60 * reflectively.
61 *
62 * @param finalizableReferenceClass FinalizableReference.class.
63 * @param queue a reference queue that the thread will poll.
64 * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be
65 * queued either when the FinalizableReferenceQueue is no longer referenced anywhere, or when
66 * its close() method is called.
67 */
68 public static void startFinalizer(
69 Class<?> finalizableReferenceClass,
70 ReferenceQueue<Object> queue,
71 PhantomReference<Object> frqReference) {
72 /*
73 * We use FinalizableReference.class for two things:
74 *
75 * 1) To invoke FinalizableReference.finalizeReferent()
76 *
77 * 2) To detect when FinalizableReference's class loader has to be garbage
78 * collected, at which point, Finalizer can stop running
79 */
80 if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
81 throw new IllegalArgumentException(
82 "Expected " + FINALIZABLE_REFERENCE + ".");
83 }
84
85 Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference);
86 Thread thread = new Thread(finalizer);
87 thread.setName(Finalizer.class.getName());
88 thread.setDaemon(true);
89
90 try {
91 if (inheritableThreadLocals != null) {
92 inheritableThreadLocals.set(thread, null);
93 }
94 } catch (Throwable t) {
95 logger.log(Level.INFO, "Failed to clear thread local values inherited"
96 + " by reference finalizer thread.", t);
97 }
98
99 thread.start();
100 }
101
102 private final WeakReference<Class<?>> finalizableReferenceClassReference;
103 private final PhantomReference<Object> frqReference;
104 private final ReferenceQueue<Object> queue;
105
106 private static final Field inheritableThreadLocals
107 = getInheritableThreadLocalsField();
108
109 /** Constructs a new finalizer thread. */
110 private Finalizer(
111 Class<?> finalizableReferenceClass,
112 ReferenceQueue<Object> queue,
113 PhantomReference<Object> frqReference) {
114 this.queue = queue;
115
116 this.finalizableReferenceClassReference
117 = new WeakReference<Class<?>>(finalizableReferenceClass);
118
119 // Keep track of the FRQ that started us so we know when to stop.
120 this.frqReference = frqReference;
121 }
122
123 /**
124 * Loops continuously, pulling references off the queue and cleaning them up.
125 */
126 @SuppressWarnings("InfiniteLoopStatement")
127 @Override
128 public void run() {
129 while (true) {
130 try {
131 if (!cleanUp(queue.remove())) {
132 break;
133 }
134 } catch (InterruptedException e) { /* ignore */ }
135 }
136 }
137
138 /**
139 * Cleans up a single reference. Catches and logs all throwables.
140 * @return true if the caller should continue, false if the associated FinalizableReferenceQueue
141 * is no longer referenced.
142 */
143 private boolean cleanUp(Reference<?> reference) {
144 Method finalizeReferentMethod = getFinalizeReferentMethod();
145 if (finalizeReferentMethod == null) {
146 return false;
147 }
148 do {
149 /*
150 * This is for the benefit of phantom references. Weak and soft
151 * references will have already been cleared by this point.
152 */
153 reference.clear();
154
155 if (reference == frqReference) {
156 /*
157 * The client no longer has a reference to the
158 * FinalizableReferenceQueue. We can stop.
159 */
160 return false;
161 }
162
163 try {
164 finalizeReferentMethod.invoke(reference);
165 } catch (Throwable t) {
166 logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
167 }
168
169 /*
170 * Loop as long as we have references available so as not to waste
171 * CPU looking up the Method over and over again.
172 */
173 } while ((reference = queue.poll()) != null);
174 return true;
175 }
176
177 /**
178 * Looks up FinalizableReference.finalizeReferent() method.
179 */
180 private Method getFinalizeReferentMethod() {
181 Class<?> finalizableReferenceClass
182 = finalizableReferenceClassReference.get();
183 if (finalizableReferenceClass == null) {
184 /*
185 * FinalizableReference's class loader was reclaimed. While there's a
186 * chance that other finalizable references could be enqueued
187 * subsequently (at which point the class loader would be resurrected
188 * by virtue of us having a strong reference to it), we should pretty
189 * much just shut down and make sure we don't keep it alive any longer
190 * than necessary.
191 */
192 return null;
193 }
194 try {
195 return finalizableReferenceClass.getMethod("finalizeReferent");
196 } catch (NoSuchMethodException e) {
197 throw new AssertionError(e);
198 }
199 }
200
201 public static Field getInheritableThreadLocalsField() {
202 try {
203 Field inheritableThreadLocals
204 = Thread.class.getDeclaredField("inheritableThreadLocals");
205 inheritableThreadLocals.setAccessible(true);
206 return inheritableThreadLocals;
207 } catch (Throwable t) {
208 logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
209 + " Reference finalizer threads will inherit thread local"
210 + " values.");
211 return null;
212 }
213 }
214 }